Skip to content

remote: client that finds, starts and connects to fizzy - #728

Merged
foxnne merged 5 commits into
mainfrom
sdk/remote-client
Oct 21, 2026
Merged

foxnne merged 5 commits into
mainfrom
sdk/remote-client

Conversation

@foxnne

@foxnne foxnne commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Part of fizzyedit/work#41 (EMBEDDED_EDITOR_PLAN step 5, fizzyedit/work#32)

What changes

sdk/remote/Client.zig is what a host adapter (Blender, Unreal, ...) calls first. It does five things:

  • Finds the installed fizzy. FIZZY_EXE wins. Otherwise it checks the usual places per OS: /Applications and ~/Applications plus Launch Services on macOS; Velopack under %LOCALAPPDATA% plus App Paths on Windows; ~/.local, an AppImage and PATH on Linux.
  • Asks for the socket. It runs fizzy --control-path, with --profile or FIZZY_PROFILE, unless the host names the socket.
  • Starts fizzy quietly if nothing listens. It runs --quiet (macOS: open -g -j, never taking focus) and dials with backoff until timeout_ms.
  • Upgrades and says hello. It sends GET /remote and exchanges hellos.
  • Reconnects after a drop with the same steps.

Errors are distinct: NotInstalled, NotRunning, FizzyTooOld, ClientTooOld, TimedOut, StartFailed and Sandboxed. A sandboxed host that reaches neither the install nor the socket gets Sandboxed. A private second fizzy is never started.

capi.zig and fizzy_remote.h are the C ABI: open, reconnect, send/recv whole frames, close.

The file is Client.zig, not client.zig as the kickoff named it, because the lint file-alias rule requires a struct file to be TitleCase. build/app.zig now builds the fake fizzy and hands its path to the remote tests through an options module; this replaces the old generic list entry.

Over the soft 800-line limit: about 900 lines outside tests and docs. That counts the fake fizzy (about 120 lines) and a mostly-comment header (about 100 lines).

SDK impact

  • None (nothing under sdk/src/**, no fingerprint move)

Verified

Tests run against fake_fizzy.zig, an executable listening on a test socket. They cover found without a second start, quiet start, not installed / not running / sandboxed, slow start, timeout then found later, crash during start (StartFailed), version skew both ways, and drop then reconnect. The C ABI is tested on top of the same fake.

  • macOS: not tried by hand. The module cross-compiles for aarch64-macos, and candidates is tested as strings. mdfind and open -g -j are untested.

  • Windows: not tried by hand. The module cross-compiles for x86_64-windows. reg query output parsing is tested; the registry lookup itself is not.

  • Linux: all of these pass:

    • scripts/check-fmt.sh
    • zb build test (35/35 remote tests)
    • test-integration
    • check-web
    • test-sdk-version
    • lint
    • check-tests-reached

    The remote tests also pass natively with -lc.

  • Web: not applicable. check-web passes.

Follow-ups

  • Build the C ABI as a shared library target. For now it is compiled and tested through the test root.
  • Fizzy's side of GET /remote (plan steps 3 and 4) is not here. The tests use the fake.
  • A fizzy started by a connect that then failed (TimedOut, failed upgrade) is reaped when it exits but not killed, so it can outlive the failure.

Generated by Claude Code

claude and others added 4 commits October 20, 2026 22:29
Client.zig locates fizzy per OS (FIZZY_EXE, the usual install places, Launch Services,
App Paths), asks it for its socket (--control-path), starts it with --quiet when nothing
listens (macOS: open -g -j) and dials with backoff to a deadline, then upgrades to the
remote-view stream and exchanges hellos. Distinct errors: not installed, fizzy too old,
add-on too old, timed out, start failed, sandboxed (never a private second fizzy).
reconnect runs the same steps after a drop. capi.zig and fizzy_remote.h are the C ABI.

Tested against fake_fizzy.zig, an executable on a test socket: found, not found, quiet
start, slow start, crash during start, version skew, drop and reconnect.

Part of fizzyedit/work#41

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KLaaExP2YTZiSnff98PmgV
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KLaaExP2YTZiSnff98PmgV
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KLaaExP2YTZiSnff98PmgV
A bye closed the stream but left the endpoint, so connected() stayed true and the next poll
unwrapped a null stream. connected() now needs the stream, and poll and send finish the close
(Closed) when the stream is gone; the endpoint is kept until then because the bye's strings
borrow it. New fake mode `bye` and a test polling and sending after a bye.

locate now tries the $PATH search (onPath) last, after the fixed places and the system lookup,
instead of inside candidates, so a stray fizzy on the path no longer beats the install.

A fizzy that startQuiet launched directly (not through open) is now waited for on a detached
thread once connected, so its exit leaves no zombie.

poll reads the version skew before closing the endpoint it lives in. The bye's borrow is
documented on poll and closeAfterBye. connect now reaps a fizzy it started on every exit, a
timeout or failed upgrade included (not killed: a slow one may still be found later).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the sdk/remote-client branch from 60bd283 to 9a094aa Compare October 21, 2026 18:59
@foxnne
foxnne marked this pull request as ready for review October 21, 2026 19:31
@foxnne
foxnne added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit caa65b5 Oct 21, 2026
11 checks passed
@foxnne
foxnne deleted the sdk/remote-client branch October 21, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants